import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import mplfinance as mpf
from matplotlib.font_manager import FontProperties
import matplotlib.dates as mdates

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

print("比亚迪股票价格走势分析与可视化")
print("="*50)

# 任务1：读取数据，观察数据形状，输出前5行，并进行描述性分析
print("\n任务1：数据读取与基本分析")
print("-"*30)
try:
    # 读取Excel文件
    file_path = "比亚迪后复权历史行情数据.xlsx"
    df = pd.read_excel(file_path)
    
    # 观察数据形状
    print(f"数据形状: {df.shape}")
    print(f"行数: {df.shape[0]}, 列数: {df.shape[1]}")
    
    # 输出数据类型
    print("\n数据类型:")
    print(df.dtypes)
    
    # 输出前5行
    print("\n数据前5行:")
    print(df.head())
    
    # 对定量变量进行描述性分析
    print("\n描述性统计分析:")
    numeric_cols = df.select_dtypes(include=[np.number]).columns
    desc_stats = df[numeric_cols].describe()
    print(desc_stats)
    
    # 检查是否有日期列并进行处理
    # 假设日期列可能命名为'日期', '时间', 'Date', 'datetime'等
    date_cols = [col for col in df.columns if any(keyword in col.lower() for keyword in ['日期', '时间', 'date', 'datetime'])]
    
    if date_cols:
        date_col = date_cols[0]
        print(f"\n找到日期列: {date_col}")
        df[date_col] = pd.to_datetime(df[date_col])
        df.set_index(date_col, inplace=True)
        print(f"已将{date_col}设为索引")
    else:
        print("\n未找到明显的日期列，使用默认索引")
        
except Exception as e:
    print(f"读取数据时出错: {e}")
    # 如果读取失败，创建一个简单的模拟数据集用于演示
    print("创建模拟数据集进行演示...")
    dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='B')
    np.random.seed(42)
    data = {
        '开盘价': 250 + np.cumsum(np.random.randn(len(dates)) * 10),
        '收盘价': 250 + np.cumsum(np.random.randn(len(dates)) * 10),
        '最高价': lambda x: x['开盘价'] * (1 + np.random.rand(len(x)) * 0.02),
        '最低价': lambda x: x['开盘价'] * (1 - np.random.rand(len(x)) * 0.02),
        '成交量': np.random.randint(1000000, 10000000, len(dates))
    }
    df = pd.DataFrame(index=dates)
    df['开盘价'] = data['开盘价']
    df['收盘价'] = data['收盘价']
    df['最高价'] = df['开盘价'] * (1 + np.random.rand(len(df)) * 0.02)
    df['最低价'] = df['开盘价'] * (1 - np.random.rand(len(df)) * 0.02)
    df['成交量'] = data['成交量']
    print(f"模拟数据形状: {df.shape}")
    print("模拟数据前5行:")
    print(df.head())
    print("\n描述性统计分析:")
    print(df.describe())

# 任务2：计算相关系数并绘制热力图
print("\n任务2：相关性分析")
print("-"*30)
try:
    # 计算相关系数
    corr_matrix = df.select_dtypes(include=[np.number]).corr()
    print("相关系数矩阵:")
    print(corr_matrix)
    
    # 绘制相关系数热力图
    plt.figure(figsize=(10, 8))
    sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', linewidths=0.5, fmt='.3f')
    plt.title('比亚迪股票各变量相关系数热力图')
    plt.tight_layout()
    plt.savefig('相关系数热力图.png', dpi=300)
    print("相关系数热力图已保存为'相关系数热力图.png'")
    plt.close()
    
except Exception as e:
    print(f"相关性分析时出错: {e}")

# 任务3：绘制2023年全年收盘价时序图
print("\n任务3：2023年全年收盘价时序图")
print("-"*30)
try:
    # 确保索引是日期类型
    if not isinstance(df.index, pd.DatetimeIndex):
        print("索引不是日期类型，尝试转换...")
        # 假设第一列是日期
        if len(df.columns) > 0:
            df[df.columns[0]] = pd.to_datetime(df[df.columns[0]])
            df.set_index(df.columns[0], inplace=True)
    
    # 筛选2023年数据
    if isinstance(df.index, pd.DatetimeIndex):
        df_2023 = df[df.index.year == 2023]
    else:
        df_2023 = df.copy()
    
    # 查找收盘价列
    close_cols = [col for col in df_2023.columns if any(keyword in col.lower() for keyword in ['收盘', 'close'])]
    
    if close_cols:
        close_col = close_cols[0]
    else:
        # 如果没有明显的收盘价列，使用第一个数值列
        close_col = df_2023.select_dtypes(include=[np.number]).columns[0]
    
    print(f"使用列 '{close_col}' 作为收盘价")
    
    # 绘制时序图
    plt.figure(figsize=(14, 7))
    plt.plot(df_2023.index, df_2023[close_col], '-b', linewidth=1.5)
    plt.title('比亚迪2023年收盘价时序图')
    plt.xlabel('日期')
    plt.ylabel('收盘价')
    plt.grid(True, linestyle='--', alpha=0.7)
    
    # 设置日期格式
    if isinstance(df_2023.index, pd.DatetimeIndex):
        plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
        plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
        plt.xticks(rotation=45)
    
    plt.tight_layout()
    plt.savefig('2023年收盘价时序图.png', dpi=300)
    print("2023年收盘价时序图已保存为'2023年收盘价时序图.png'")
    plt.close()
    
except Exception as e:
    print(f"绘制时序图时出错: {e}")

# 任务4：绘制2023年4-6月的K线图
print("\n任务4：2023年4-6月K线图")
print("-"*30)
try:
    # 确保索引是日期类型
    if not isinstance(df.index, pd.DatetimeIndex):
        print("索引不是日期类型，无法绘制K线图")
        raise ValueError("需要日期索引来绘制K线图")
    
    # 筛选2023年4-6月数据
    df_kline = df[(df.index.year == 2023) & (df.index.month >= 4) & (df.index.month <= 6)]
    
    if df_kline.empty:
        print("未找到2023年4-6月的数据，使用部分数据进行演示")
        # 使用最近的60天数据
        df_kline = df.tail(60)
    
    print(f"K线图数据范围: {df_kline.index.min()} 到 {df_kline.index.max()}")
    
    # 查找OHLC列
    ohlc_cols = {}
    possible_names = {
        'open': ['开盘', 'open', 'Open'],
        'high': ['最高', 'high', 'High'],
        'low': ['最低', 'low', 'Low'],
        'close': ['收盘', 'close', 'Close'],
        'volume': ['成交量', 'volume', 'Volume']
    }
    
    for key, keywords in possible_names.items():
        found = False
        for col in df_kline.columns:
            if any(keyword in col.lower() for keyword in keywords):
                ohlc_cols[key] = col
                found = True
                break
        if not found and key != 'volume':  # 成交量是可选的
            print(f"警告: 未找到'{key}'列")
    
    # 检查是否有足够的列来绘制K线图
    required_cols = ['open', 'high', 'low', 'close']
    if not all(col in ohlc_cols for col in required_cols):
        print("未找到足够的OHLC列，使用默认列名")
        # 使用前四列数值列
        numeric_cols = df_kline.select_dtypes(include=[np.number]).columns
        if len(numeric_cols) >= 4:
            ohlc_cols = {
                'open': numeric_cols[0],
                'high': numeric_cols[1],
                'low': numeric_cols[2],
                'close': numeric_cols[3]
            }
            if len(numeric_cols) >= 5:
                ohlc_cols['volume'] = numeric_cols[4]
        else:
            raise ValueError("数据中没有足够的数值列来绘制K线图")
    
    # 重命名列以适应mplfinance
    kline_data = df_kline.rename(columns={
        ohlc_cols['open']: 'Open',
        ohlc_cols['high']: 'High',
        ohlc_cols['low']: 'Low',
        ohlc_cols['close']: 'Close'
    })
    
    if 'volume' in ohlc_cols:
        kline_data = kline_data.rename(columns={ohlc_cols['volume']: 'Volume'})
    
    # 绘制K线图
    kwargs = {
        'type': 'candle',
        'style': 'yahoo',
        'title': '比亚迪2023年4-6月K线图',
        'ylabel': '价格',
        'figratio': (14, 7),
        'figscale': 1.2
    }
    
    if 'Volume' in kline_data.columns:
        kwargs['volume'] = True
        kwargs['ylabel_lower'] = '成交量'
    
    mpf.plot(kline_data, **kwargs, savefig='2023年4-6月K线图.png')
    print("2023年4-6月K线图已保存为'2023年4-6月K线图.png'")
    
    # K线图分析
    print("\nK线图分析:")
    # 计算基本统计量
    price_change = kline_data['Close'].iloc[-1] - kline_data['Close'].iloc[0]
    price_change_pct = (price_change / kline_data['Close'].iloc[0]) * 100
    avg_volume = kline_data['Volume'].mean() if 'Volume' in kline_data.columns else None
    
    print(f"期间价格变动: {price_change:.2f} ({price_change_pct:.2f}%)")
    if avg_volume:
        print(f"平均成交量: {avg_volume:,.0f}")
    
    # 趋势分析
    if price_change_pct > 5:
        trend = "上涨趋势"
    elif price_change_pct < -5:
        trend = "下跌趋势"
    else:
        trend = "震荡走势"
    
    print(f"整体趋势: {trend}")
    
    # 波动性分析
    daily_returns = kline_data['Close'].pct_change().dropna()
    volatility = daily_returns.std() * 100
    print(f"日收益率波动率: {volatility:.2f}%")
    
    # 支撑阻力位分析（简化版）
    support = kline_data['Low'].min()
    resistance = kline_data['High'].max()
    print(f"支撑位（期间最低价）: {support:.2f}")
    print(f"阻力位（期间最高价）: {resistance:.2f}")
    
except Exception as e:
    print(f"绘制K线图时出错: {e}")

print("\n比亚迪股票价格走势分析与可视化完成！")
print("生成的图表文件:")
print("1. 相关系数热力图.png")
print("2. 2023年收盘价时序图.png")
print("3. 2023年4-6月K线图.png")